package com.senlingspace.web.controller.payment;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.senlingspace.common.core.controller.BaseController;
import com.senlingspace.common.core.domain.AjaxResult;
import com.senlingspace.common.core.redis.RedisLock;
import com.senlingspace.common.request.PaymentRequest;
import com.senlingspace.common.request.RepaymentRequest;
import com.senlingspace.common.utils.SecurityUtils;
import com.senlingspace.payment.core.refund.IRefundService;
import com.senlingspace.payment.core.ConcreteOrderProcess;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.DigestUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 支付
 *
 * @author lebronjames
 * @date 2024/07/26
 */
@Slf4j
@Validated
@AllArgsConstructor
@RestController
@RequestMapping(path = "/payment/api")
public class SlkjPaymentController extends BaseController {

    private final IRefundService iRefundService;
    private final RedisLock redisLock;
    private final ConcreteOrderProcess concreteOrderProcess;


    /**
     * 支付
     *
     * @param paymentRequest 支付请求
     * @return {@link AjaxResult }
     */
    @PostMapping(path = "/payment")
    public AjaxResult payment(@Valid @RequestBody PaymentRequest paymentRequest) {
        log.info("用户主动出示付款码支付，入参：{}", JSON.toJSONString(paymentRequest));
        Long userId = super.getUserId();
        log.info("操作人：{}，ID：{}。", super.getUsername(), userId);
        if (Objects.isNull(userId)) {
            return AjaxResult.error("用户未登录！");
        }

        String reqHash = DigestUtils.md5DigestAsHex(paymentRequest.toString().getBytes(StandardCharsets.UTF_8));
        String lockKey = "PAYMENT_LOCK:" + reqHash + "_" + userId;
        RLock lock = redisLock.getRLock(lockKey);

        boolean isLocked = false;
        try {
            isLocked = lock.tryLock(15, TimeUnit.SECONDS);
            if (isLocked) {
                return concreteOrderProcess.processOrder(paymentRequest, SecurityUtils.getUserId() + "");
            } else {
                log.warn("用户重复提交！锁超时：{}", lockKey);
                return AjaxResult.error("请勿重复下单！");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("处理支付请求时发生异常：{}", e.getMessage(), e);
            return AjaxResult.error("下单失败，请刷新重试！");
        } finally {
            if (isLocked && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }


    /**
     * 退款
     *
     * @param request
     * @return {@link AjaxResult }
     */
    @PreAuthorize("@ss.hasPermi('system:order:refund')")
    @PostMapping(path = "/refund")
    public AjaxResult refundOrder(@RequestBody JSONObject request) {
        log.info("退款，入参：{}", request);
        if (Objects.isNull(request)) {
            return AjaxResult.error("订单ID不能为空！");
        }

        Long userId = super.getUserId();
        log.info("操作人：{}，ID：{}。", super.getUsername(), userId);
        if (Objects.isNull(super.getUserId())) {
            return AjaxResult.error("用户未登录！");
        }

        String lockKey = "REFUND_LOCK:" + request.getString("orderId");
        RLock lock = redisLock.getRLock(lockKey);

        boolean isLocked = false;
        try {
            isLocked = lock.tryLock(15, TimeUnit.SECONDS);
            if (isLocked) {
                return iRefundService.refundOrder(request.getString("orderId"), userId);
            }
            log.warn("用户重复提交！锁超时：{}", lockKey);
            return AjaxResult.error("请勿重复退款！");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("处理退款请求时发生异常：{}", e.getMessage(), e);
            return AjaxResult.error("退款失败，请刷新重试！");
        } finally {
            if (isLocked && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 重新支付
     *
     * @param request
     * @return {@link AjaxResult }
     */
    @PostMapping(path = "/repayment")
    public AjaxResult repayment(@Valid @RequestBody RepaymentRequest request) {
        log.info("重新支付，入参：{}", JSON.toJSONString(request));

        log.info("操作人：{}，ID：{}。", super.getUsername(), super.getUserId());
        if (Objects.isNull(super.getUserId())) {
            return AjaxResult.error("用户未登录！");
        }

        String lockKey = "REPAYMENT_LOCK:" + request.getOrderId();
        RLock lock = redisLock.getRLock(lockKey);
        boolean isLocked = false;
        try {
            isLocked = lock.tryLock(15, TimeUnit.SECONDS);
            if (isLocked) {
                return concreteOrderProcess.processRepayment(request, SecurityUtils.getUserId() + "");
            }
            log.warn("用户重复提交！锁超时：{}", lockKey);
            return AjaxResult.error("请勿重复提交！");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("处理重新支付请求时发生异常：{}", e.getMessage(), e);
            return AjaxResult.error("重新支付失败，请刷新重试！");
        } finally {
            if (isLocked && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

}
